package com.cameron.serviceedu.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cameron.entity.*;
import com.cameron.servicebase.config.exception.BusinessException;
import com.cameron.serviceedu.client.VodClient;
import com.cameron.serviceedu.entity.*;
import com.cameron.serviceedu.entity.dto.CourseDto;
import com.cameron.serviceedu.entity.dto.front.CourseInfoVo;
import com.cameron.serviceedu.entity.viewobject.CourseCondition;
import com.cameron.serviceedu.entity.viewobject.CourseVo;
import com.cameron.serviceedu.mapper.EduCourseMapper;
import com.cameron.serviceedu.service.*;
import com.cameron.utils.JwtUtils;
import com.cameron.utils.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.cameron.constant.CourseConstant.*;
import static com.cameron.constant.RoleConstant.*;

/**
 * <p>
 * 课程 服务实现类
 * </p>
 *
 * @author cameron
 * @since 2022-06-17
 */
@Slf4j
@Service
public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {

    @Resource
    private EduCourseDescriptionService descriptionService;

    @Resource
    private EduChapterService chapterService;

    @Resource
    private EduVideoService subChapterService;

    @Resource
    private VodClient vodClient;

    @Resource
    private EduTeacherService teacherService;

    @Resource
    private EduSubjectService subjectService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    @Override
    @Transactional
    public Result saveOrUpdate(CourseDto courseDto) {
        //将数据添加或修改到数据库中
        EduCourse course = new EduCourse();
        BeanUtils.copyProperties(courseDto, course);
        course.setStatus(NOT_COMPLETE);
        boolean saved = this.saveOrUpdate(course);
        //添加后获取课程id
        String id = course.getId();
        //将描述信息添加或修改到数据库中
        EduCourseDescription description = new EduCourseDescription();
        description.setId(id);
        description.setDescription(courseDto.getDescription());
        saved = saved && descriptionService.saveOrUpdate(description);
        return saved ? Result.ok().data("courseId", id) : Result.error().message("课程信息添加失败");
    }

    @Override
    @Transactional
    public Result getCourse(String courseId) {

        //查询课程表
        EduCourse course = this.getById(courseId);
        if (course == null) {
            return Result.error().message("没有该课程信息！");
        }
        //通过课程id获取课程描述信息
        LambdaQueryWrapper<EduCourseDescription> lqw = new LambdaQueryWrapper<>();
        lqw.eq(EduCourseDescription::getId, courseId);
        EduCourseDescription description = descriptionService.getOne(lqw);

        //将课程信息和课程描述信息封装到Dto中
        CourseDto courseDto = new CourseDto();
        BeanUtils.copyProperties(course, courseDto);
        courseDto.setDescription(description.getDescription());

        //返回数据
        return Result.ok().data("courseInfo", courseDto);
    }

    @Override
    public Result publish(String courseId) {
        EduCourse course = new EduCourse();
        course.setId(courseId);
        course.setStatus(PUBLISHED);
        boolean updated = this.updateById(course);
        return updated ? Result.ok() : Result.error().message("发布失败");
    }

    @Override
    @SuppressWarnings("unchecked")
    public Result pageByConditions(CourseCondition condition, int currentPage, int size) {
        Page<EduCourse> page = new Page<>(currentPage, size);
        LambdaQueryWrapper<EduCourse> lqw = new LambdaQueryWrapper<>();
        //避免空指针
        condition = Optional.ofNullable(condition).orElseGet(CourseCondition::new);
        //对课程标题进行判空并筛选
        String title = condition.getTitle();
        String status = condition.getStatus();
        //模糊查询
        lqw.like(StringUtils.isNotEmpty(title), EduCourse::getTitle, title)
                .eq(StringUtils.isNotEmpty(status), EduCourse::getStatus, status)
                //过滤半成品
                .ne(EduCourse::getStatus, NOT_COMPLETE)
                .orderByDesc(EduCourse::getGmtModified);
        this.page(page, lqw);
        List<EduCourse> courseList = page.getRecords();

        //包装为ViewObject
        ArrayList<BackendCourseVo> records = courseList.stream().map(record -> {
            BackendCourseVo courseVo = new BackendCourseVo();
            BeanUtils.copyProperties(record, courseVo);
            return courseVo;
        }).collect(Collectors.toCollection(ArrayList::new));

        //收集讲师姓名
        Map<String, String> teacherNames = teacherService.listByIds(records
                        .stream().map(BackendCourseVo::getTeacherId)
                        .collect(Collectors.toCollection(ArrayList::new)))
                //根据id作为key name作为value收集为Map
                .stream()
                .collect(Collectors.toMap(EduTeacher::getId, EduTeacher::getName));
        //将讲师姓名封装
        records.forEach(record -> record.setTeacherName(teacherNames.get(record.getTeacherId())));

        return Result.ok().data("page", records).data("total", page.getTotal());
    }


    @Override
    @Transactional
    public Result removeCourseById(String courseId) {
        String token = stringRedisTemplate.opsForValue().get(TOKEN);
        BackendMemberDto memberDto;
        if (StringUtils.isEmpty(token) || (memberDto = JwtUtils.getLoginMemberByToken(token)) == null) {
            throw new BusinessException(50000, "登录过期");
        }
        Integer roles = memberDto.getRoles();
        //判断是否有权限
        if (roles != MEMBER_ADMIN) {
            EduTeacher eduTeacher = teacherService.getById(memberDto.getId());
            if (eduTeacher == null) throw new BusinessException(40003, "无权限！");
            EduCourse course = this.getById(courseId);
            if (!course.getTeacherId().equals(eduTeacher.getId())) {
                throw new BusinessException(40003, "不可删除他人课程");
            }
        }
        //删除子章节信息
        LambdaQueryWrapper<EduVideo> videoWrapper = new LambdaQueryWrapper<>();
        videoWrapper.eq(EduVideo::getCourseId, courseId);
        //查询该课程下所有的子章节信息
        List<EduVideo> list = subChapterService.list(videoWrapper);
        String videoIds = list.stream().map(EduVideo::getVideoSourceId)
                .collect(Collectors.joining(","));
        //删除小节视频
        if (StringUtils.isNotBlank(videoIds)) {
            Result result = vodClient.deleteVideo(videoIds);
            log.info(result.getSuccess().equals(Boolean.TRUE) ? "删除视频成功" : "删除失败：" + result.getMessage());
        }
        //删除视频后将小节数据删除
        subChapterService.remove(videoWrapper);
        //删除小节后将章节数据删除
        LambdaQueryWrapper<EduChapter> chapterWrapper = new LambdaQueryWrapper<>();
        chapterWrapper.eq(EduChapter::getCourseId, courseId);
        chapterService.remove(chapterWrapper);
        //根据课程id删除课程描述信息
        LambdaQueryWrapper<EduCourseDescription> descriptionWrapper = new LambdaQueryWrapper<>();
        descriptionWrapper.eq(EduCourseDescription::getId, courseId);
        descriptionService.remove(descriptionWrapper);
        //最后删除课程信息
        this.removeById(courseId);
        return Result.ok().message("删除成功！");
    }


    @Deprecated
    @Transactional
    public Result removeCourseByIdOld(String courseId) {
        String token = stringRedisTemplate.opsForValue().get("token");
        BackendMemberDto memberDto;
        if (StringUtils.isEmpty(token) || (memberDto = JwtUtils.getLoginMemberByToken(token)) == null) {
            throw new BusinessException(50000, "登录过期");
        }
        Integer roles = memberDto.getRoles();

        if (roles != MEMBER_ADMIN) {
            EduTeacher eduTeacher = teacherService.getById(memberDto.getId());
            if (eduTeacher == null) throw new BusinessException(40003, "无权限！");
            EduCourse course = this.getById(courseId);
            if (!course.getTeacherId().equals(eduTeacher.getId())) {
                throw new BusinessException(40003, "不可删除他人课程");
            }
        }

        //删除子章节信息
        LambdaQueryWrapper<EduVideo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(EduVideo::getCourseId, courseId);
        //查询该课程下所有的子章节信息
        List<EduVideo> list = subChapterService.list(queryWrapper);
        StringBuilder sb = new StringBuilder();
        boolean success = true;
        //若该课程下有子章节 则删出子章节中的视频 反之不做处理
        if (list != null) {
            for (EduVideo eduVideo : list) {
                String sourceId = eduVideo.getVideoSourceId();
                if (StringUtils.isNotEmpty(sourceId)) {
                    sb.append(sourceId).append(",");
                }
            }
            //判断子章节中是否含有视频
            if (sb.length() != 0) {
                String videoIds = sb.substring(0, sb.length() - 1);
                success = vodClient.deleteVideo(videoIds).getSuccess();
            }
            //删除视频后将子章节删除
            LambdaQueryWrapper<EduVideo> videoWrapper = new LambdaQueryWrapper<>();
            videoWrapper.eq(EduVideo::getCourseId, courseId);
            success = success && subChapterService.remove(videoWrapper);
        }

        //根据课程id删除章节
        LambdaQueryWrapper<EduChapter> chapterWrapper = new LambdaQueryWrapper<>();
        chapterWrapper.eq(EduChapter::getCourseId, courseId);
        success = success && chapterService.remove(chapterWrapper);

        //根据课程id删除描述信息
        LambdaQueryWrapper<EduCourseDescription> descriptionWrapper = new LambdaQueryWrapper<>();
        descriptionWrapper.eq(EduCourseDescription::getId, courseId);
        success = success && descriptionService.remove(descriptionWrapper);

        //删除课程
        success = success && this.removeById(courseId);

        return success ? Result.ok() : Result.error();
    }

    @Override
    public CourseInfoVo getCourseInfo(String courseId) {

        EduCourse course = this.getById(courseId);
        //查询该课程是否存在
        if (course == null) {
            return null;
        }
        //创建返回对象vo
        CourseInfoVo courseInfo = new CourseInfoVo();
        //拷贝查询出的课程对象
        BeanUtils.copyProperties(course, courseInfo);

        //查询课程对应的讲师id
        String teacherId = course.getTeacherId();
        if (StringUtils.isNotEmpty(teacherId)) {
            EduTeacher teacher = teacherService.getById(teacherId);
            courseInfo.setTeacherId(teacherId);
            courseInfo.setTeacherName(teacher.getName());
            courseInfo.setAvatar(teacher.getAvatar());
            courseInfo.setIntro(teacher.getIntro());
        }

        //查询课程描述信息
        LambdaQueryWrapper<EduCourseDescription> lqw = new LambdaQueryWrapper<>();
        lqw.eq(EduCourseDescription::getId, courseId);
        EduCourseDescription description = descriptionService.getOne(lqw);

        courseInfo.setDescription(description == null ? null : description.getDescription());

        //查询课程的学科信息
        LambdaQueryWrapper<EduSubject> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(EduSubject::getId, course.getSubjectId());
        EduSubject subject = subjectService.getOne(wrapper);

        courseInfo.setSubjectLevelTwoId(subject.getId());
        courseInfo.setSubjectLevelTwo(subject.getTitle());

        //查询一级学科的信息
        String parentId = subject.getParentId();
        LambdaQueryWrapper<EduSubject> courseWrapper = new LambdaQueryWrapper<>();
        wrapper.eq(EduSubject::getParentId, parentId);
        EduSubject parent = subjectService.getOne(courseWrapper);

        courseInfo.setSubjectLevelOneId(parentId);
        courseInfo.setSubjectLevelOne(parent.getTitle());

        return courseInfo;
    }

    @Override
    public CourseVo getCourseDetail(String courseId) {
        String token = stringRedisTemplate.opsForValue().get("token");
        if (StringUtils.isEmpty(token)) {
            throw new BusinessException(50000, "登录会话过期! 请重新登录!");
        }
        BackendMemberDto memberDto = JwtUtils.getLoginMemberByToken(token);
        if (memberDto == null) {
            throw new BusinessException(50000, "登录会话过期! 请重新登录!");
        }
        String memberId = memberDto.getId();
        Integer roles = memberDto.getRoles();

        EduCourse course = this.getById(courseId);
        if (course == null) throw new BusinessException(404, "没有该课程！");
        String teacherId = course.getTeacherId();
        //若当前用户既不是管理员,也不是该课程的讲师,则无权限修改
        if (roles != MEMBER_ADMIN && !teacherId.equals(memberId)) {
            throw new BusinessException(40005, "无权限修改他人的课程!!");
        }

        EduTeacher teacher = teacherService.getById(teacherId);
        boolean hasTeacher = teacher != null;
        CourseVo courseVo = new CourseVo();
        courseVo.setId(course.getId());
        courseVo.setCover(course.getCover());
        courseVo.setSubjectId(course.getSubjectId());
        courseVo.setSubjectParentId(course.getSubjectParentId());
        courseVo.setTeacherName(hasTeacher ? teacher.getName() : "");
        courseVo.setPrice(course.getPrice().doubleValue());
        courseVo.setTitle(course.getTitle());
        courseVo.setStatus(course.getStatus());
        return courseVo;
    }

    @Override
    public void updateCourse(CourseVo courseVo) {
        String id = courseVo.getId();
        String cover = courseVo.getCover();
        String subjectId = courseVo.getSubjectId();
        String subjectParentId = courseVo.getSubjectParentId();
        String teacherName = courseVo.getTeacherName();
        Double price = courseVo.getPrice();
        String title = courseVo.getTitle();
        EduCourse eduCourse = new EduCourse();
        eduCourse.setId(id);
        LambdaQueryWrapper<EduTeacher> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(EduTeacher::getName, teacherName);
        EduTeacher teacher = teacherService.getOne(wrapper);
        if (teacher == null) throw new BusinessException(404, "请输入存在的讲师");
        eduCourse.setTeacherId(teacher.getId());
        eduCourse.setSubjectId(subjectId);
        eduCourse.setSubjectParentId(subjectParentId);
        eduCourse.setTitle(title);
        eduCourse.setPrice(new BigDecimal(price));
        eduCourse.setCover(cover);
        eduCourse.setStatus(courseVo.getStatus());
        this.updateById(eduCourse);
    }

    @Override
    public List<EduCourse> getCourseLikeName(String courseName) {
        if (StringUtils.isEmpty(courseName)) {
            throw new BusinessException(40000, "请输入课程名称");
        }
        LambdaQueryWrapper<EduCourse> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(EduCourse::getTitle, courseName).last("limit 8");
        return this.list(wrapper);
    }

    @Override
    public Map<String, Object> getCourseList(int currentPage, int pageSize, com.cameron.serviceedu.entity.dto.front.CourseDto courseDto) {
        Page<EduCourse> page = new Page<>(currentPage, pageSize);

        LambdaQueryWrapper<EduCourse> lqw = null;

        if (courseDto != null) {
            lqw = new LambdaQueryWrapper<>();
            //一级分类
            lqw.eq(StringUtils.isNotEmpty(courseDto.getSubjectParentId()), EduCourse::getSubjectParentId, courseDto.getSubjectParentId())
                    //二级分类
                    .eq(StringUtils.isNotEmpty(courseDto.getSubjectId()), EduCourse::getSubjectId, courseDto.getSubjectId())
                    //根据观看数量
                    .orderByDesc(ObjectUtils.isNotEmpty(courseDto.getViewCount()), EduCourse::getViewCount)
                    //根据价格
                    //.orderByDesc(StringUtils.isNotEmpty(courseDto.getPriceSort()), EduCourse::getPrice)
                    //根据创建时间排序
                    .orderByDesc(StringUtils.isNotEmpty(courseDto.getGmtCreateSort()), EduCourse::getGmtCreate);
        }

        this.page(page, lqw);

        List<EduCourse> records = page.getRecords();
        long total = page.getTotal();
        long current = page.getCurrent();
        long pages = page.getPages();
        long size = page.getSize();
        boolean hasNext = page.hasNext();
        boolean hasPrevious = page.hasPrevious();

        HashMap<String, Object> map = new HashMap<>();

        map.put("items", records);
        map.put("current", current);
        map.put("pages", pages);
        map.put("size", size);
        map.put("total", total);
        map.put("hasNext", hasNext);
        map.put("hasPrevious", hasPrevious);
        return map;
    }


}
